package com.mantou.scucorse.core.scheduler;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;

import org.apache.commons.httpclient.HttpException;
import org.apache.commons.io.IOUtils;
import org.dom4j.DocumentException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.gargoylesoftware.htmlunit.CookieManager;
import com.mantou.cdl.extractor.XmlModeFetcher;
import com.mantou.cdl.net.HttpInvoker;
import com.mantou.cdl.net.HttpResult;
import com.mantou.scucorse.core.entity.Cookie;
import com.mantou.scucorse.core.entity.ScuCourse;
import com.mantou.scucorse.core.entity.Task;
import com.mantou.scucorse.core.entity.User;
import com.mantou.scucorse.core.service.CookieService;
import com.mantou.scucorse.core.service.ScuCourseService;
import com.mantou.scucorse.core.service.TaskService;
import com.mantou.scucorse.core.service.UserService;
import com.mantou.scucorse.utils.SystemInfo;
import com.mantou.scucorse.utils.TaskStatus;
import com.mantou.scucorse.utils.UserSatus;

/*
 * @author DengDeijia
 * this class designed to access Academic or SiChuan University
 */
public class CourseNetInvoker {

	@Autowired
	private  ScuCourseService scuCourseService;

	@Autowired
	private  UserService userService;

	@Autowired
	private  CookieService cookieService;
	@Autowired
	private  TaskService taskService;

	/*private static String[] testurls = {
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=-1",//选课查询
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=-2",//
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=1",//指导性教学计划开课信息
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=2",//培养方案开课信息
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=3",//校任选开课信息
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=4",//系任选开课信息
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=5",//课程查询结果
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=6",//学生选课结果
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=7",//选课结果维护
		"http://zhjw.scu.edu.cn/xkAction.do?actionType=8"//
		
	};*/

	private static String WEBURL="http://zhjw.scu.edu.cn/";

	private static XmlModeFetcher courseXmlModeFetcher = null;
	private static XmlModeFetcher userNameXmlModeFetcher = null;
	static{
		try {
			courseXmlModeFetcher = new XmlModeFetcher(IOUtils.toString(CourseNetInvoker.class.getResourceAsStream("/scucoursequerytemplete.xml")));
			userNameXmlModeFetcher = new XmlModeFetcher(IOUtils.toString(CourseNetInvoker.class.getResourceAsStream("/usernamefetchertemplete.xml")));
		} catch (DocumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


	public  ScuCourseService getScuCourseService() {
		return scuCourseService;
	}

	public  void setScuCourseService(ScuCourseService scuCourseService) {
		this.scuCourseService = scuCourseService;
	}

	public UserService getUserService() {
		return userService;
	}

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public CookieService getCookieService() {
		return cookieService;
	}

	public void setCookieService(CookieService cookieService) {
		this.cookieService = cookieService;
	}

	public TaskService getTaskService() {
		return taskService;
	}

	public void setTaskService(TaskService taskService) {
		this.taskService = taskService;
	}

	public  ScuCourse updateCourseInfo(int ScuCourseID){
		ScuCourse retCourse = scuCourseService.findByPrimaryKey(ScuCourseID);
		if(retCourse == null){
			return null;
		}
		//get a User context which hold a Course Task,if no user will execute this task ,choose one from all user collection
		User user = userService.findRadomByCourse(ScuCourseID);
		try {	
			if(user.getStatus() == UserSatus.NEW.getStatus() ||
					user.getStatus() == UserSatus.OFFLINE.getStatus()){
				if(0 != login(user.getId())){
					return null;
				}
				user.setStatus(UserSatus.ONLNE.getStatus());
				userService.updateByPrimaryKey(user);
				user = userService.findByPrimaryKey(user.getId());
			}

			List<Cookie> cookies = cookieService.findByUserID(user.getId());
			HttpInvoker httpInvoker = new HttpInvoker(WEBURL+
					"xkAction.do?kch="+
					retCourse.getCoursenumber()+
					"&pageNumber=-2&preActionType=2&actionType=5&cxkxh=&kcm=&skjs=&kkxsjc=&skxq=&skjc=",
					0,null);
			addCookies(httpInvoker.cookieManager,cookies);
			HttpResult httpresult= httpInvoker.request();
			if(httpresult.getStatusCode()!= 200){
				return null;
			}
			String html = new String(httpresult.getResponseBody(),"gbk");
			if(html.contains(new String("请您登录后再使用".getBytes("utf-8"),"gbk"))){
				user.setFailedtimes(SystemInfo.testTimes +1);
				handleNetExecption(user);
				return null;
			}
			List<String> courseStrs = courseXmlModeFetcher.fetch(gbk2utf8(html));
			for(String jsonStr:courseStrs){
				ScuCourse scuCourse = JSONObject.parseObject(jsonStr, ScuCourse.class);
				scuCourse.setLasdupdate(new Date());
				ScuCourse queryScuCourse = new ScuCourse();
				queryScuCourse.setCoursenumber(scuCourse.getCoursenumber());
				queryScuCourse.setSycnumber(scuCourse.getSycnumber());
				ScuCourse restCourse =  scuCourseService.find(queryScuCourse );
				if(restCourse == null){
					scuCourseService.create(scuCourse);
				}else{
					scuCourse.setId(retCourse.getId());
					scuCourseService.updateByPrimaryKey(scuCourse);
				}
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			handleNetExecption(user);
		}
		return scuCourseService.findByPrimaryKey(ScuCourseID);
	}


	public  ScuCourse updateCourseInfo(String coursenumber,String syncnumber){
		ScuCourse queryScuCourse = new ScuCourse();
		queryScuCourse.setCoursenumber(coursenumber);
		queryScuCourse.setSycnumber(syncnumber);
		ScuCourse scuCourse = scuCourseService.find(queryScuCourse);
		if(scuCourse == null){
			scuCourse = new ScuCourse();
			scuCourse.setCoursename(coursenumber);
			scuCourse.setSycnumber(syncnumber);
			scuCourse.setLasdupdate(new Date());
			scuCourseService.create(scuCourse);
		}
		return updateCourseInfo(scuCourse.getId());
	}

	public boolean prehandle(Task task){
		User user = userService.findByPrimaryKey(task.getUserid());
		ScuCourse scuCourse = scuCourseService.findByPrimaryKey(task.getCourseid());
		try {
			if(user.getStatus() != UserSatus.ONLNE.getStatus()){
				if(0!= login(user.getId())){
					return false;
				}
				user.setStatus(UserSatus.ONLNE.getStatus());
				userService.updateByPrimaryKey(user);
			}

			if(task.getStatus() == TaskStatus.INITIAL.getStatus()){
				List<Cookie> cookies = cookieService.findByUserID(user.getId());
				HttpInvoker httpInvoker = new HttpInvoker(WEBURL+"xkAction.do?actionType=-1",0,null);
				addCookies(httpInvoker.cookieManager,cookies);
				httpInvoker.request();

				httpInvoker = new HttpInvoker(WEBURL+"xkAction.do?kch="
						+scuCourse.getCoursenumber()
						+"&pageNumber=-2&preActionType=2&actionType=5&cxkxh=&kcm=&skjs=&kkxsjc=&skxq=&skjc=",0,null);
				addCookies(httpInvoker.cookieManager,cookies);
				HttpResult result = httpInvoker.request();
				String html = new String(result.getResponseBody(),"gbk");
				System.out.println(html);
			}
			task.setStatus(TaskStatus.PREHANDED.getStatus());
			taskService.updateByPrimaryKey(task);
			return true;
		}
		catch(Exception e){

		}
		return false;
	}

	public  boolean selectCourse(Task task){
		User user = userService.findByPrimaryKey(task.getUserid());
		ScuCourse scuCourse = scuCourseService.findByPrimaryKey(task.getCourseid());
		try {
			if(user.getStatus() != UserSatus.ONLNE.getStatus()){
				if(0!= login(user.getId())){
					return false;
				}
				user.setStatus(UserSatus.ONLNE.getStatus());
				userService.updateByPrimaryKey(user);
			}
			if(task.getStatus() == TaskStatus.INITIAL.getStatus() && !prehandle(task)){
				return false;
			}
			if(task.getStatus() != TaskStatus.PREHANDED.getStatus()){
				task.setStatus(TaskStatus.RUNNING.getStatus());
				taskService.updateByPrimaryKey(task);
			}
			List<Cookie> cookies = cookieService.findByUserID(user.getId());
			HttpInvoker httpInvoker = new HttpInvoker(WEBURL+"xkAction.do?kcId="
					+scuCourse.getCoursenumber()
					//+"_04"
					+scuCourse.getSycnumber()
					+"&preActionType=5&actionType=9",0,null);
			addCookies(httpInvoker.cookieManager,cookies);
			HttpResult httpResult = httpInvoker.request();
			if(httpResult.getStatusCode() != 200){
				return false;
			}
			String html = new String(httpResult.getResponseBody(),"gbk");
			if(html.contains(new String("请您登录后再使用".getBytes("utf-8"),"gbk"))){
				user.setFailedtimes(SystemInfo.testTimes +1);
				handleNetExecption(user);
				return false;
			}else if(html.contains(new String("对不起、非选课阶段不允许选课".getBytes("utf-8"),"gbk"))){
				SystemInfo.taskHibrate = true;
				taskService.hiberate();
				return false;
			}
			System.out.println(html);
			if(html.contains("选课成功") || html.contains("你已经选择了课程")){
				scuCourse.setLasdupdate(new Date());
				scuCourse.setLeftnumber(scuCourse.getLeftnumber()-1);
				scuCourseService.updateByPrimaryKey(scuCourse);

				task.setStatus(TaskStatus.COMPLETE.getStatus());
				taskService.updateByPrimaryKey(task);
				return true;
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			handleNetExecption(user);
		} 
		return false;
	}

	/*
	 * 0 success,1:password error,2:timeout
	 */
	public  int login(int userid){
		User user = userService.findByPrimaryKey(userid);
		try {
			boolean haslogin = false;
			Cookie queryCookie = new Cookie();
			queryCookie.setUserid(userid);

			// if has login
			if(cookieService.selectCount(queryCookie)>0){
				/*List<Cookie> cookies = cookieService.findByUserID(userid);
				int testoffset =  new Random().nextInt();
				if(testoffset <0){
					testoffset  = -testoffset;
				}
				testoffset =testoffset%testurls.length;
				HttpInvoker httpInvoker = new HttpInvoker(testurls[testoffset],0,null);
				addCookies(httpInvoker.cookieManager,cookies);

				HttpResult httpResult = httpInvoker.request();
				String html = new String (httpResult.getResponseBody());
				if(!html.contains(new String("请您登录后再使用".getBytes("utf-8"),"gbk"))){
					haslogin = true;
				}*/
				return 0;// because of the sync of action access,we can not use random URL to test if a user is offline 
			}

			if(!haslogin){
				HttpInvoker httpInvoker = new HttpInvoker(WEBURL+"loginAction.do?zjh="
						+user.getStudentnumber()+"&mm="+user.getPassword(),0,null);
				String resultHtml = new String(httpInvoker.request().getResponseBody(),"gbk");
				if(resultHtml.contains("/menu/mainFrame.jsp")){
					//login success
					haslogin = true;
					user.setStatus(UserSatus.ONLNE.getStatus());
					Set<com.gargoylesoftware.htmlunit.util.Cookie> cookies = httpInvoker.cookieManager.getCookies();
					retriveCookies(cookies,user);
					if(user.getStudentname() == null){//complement user info
						HttpInvoker unamehttpInvoker = new HttpInvoker(WEBURL+"/menu/s_top.jsp",0,null);
						unamehttpInvoker.cookieManager = httpInvoker.cookieManager;
						try{
							
							String nameHtml = new String(unamehttpInvoker.request().getResponseBody(),"gbk");
							List<String> fetchresult = userNameXmlModeFetcher.fetch(gbk2utf8(nameHtml));
							if(fetchresult.size() >0){
								JSONObject jsonObject = JSONObject.parseObject(fetchresult.get(0));
								user.setStudentname(jsonObject.getString("username"));
							}
						}catch(Exception e){// we allow there occur network access exception,because of user name just a display name 
							//do nothing
						}
					}
				}else{
					user.setStatus(UserSatus.PASSWORDERROR.getStatus());
					haslogin = false;
				}
			}
			userService.updateByPrimaryKey(user);
			return haslogin?0:1;
		} catch (HttpException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		user.setStatus(UserSatus.OFFLINE.getStatus());
		userService.updateByPrimaryKey(user);
		return 2;
	}

	private  void retriveCookies(Set<com.gargoylesoftware.htmlunit.util.Cookie> cookies,User user){
		Iterator<com.gargoylesoftware.htmlunit.util.Cookie> it = cookies.iterator();
		while(it.hasNext()){
			com.gargoylesoftware.htmlunit.util.Cookie innercookie = it.next();
			Cookie querycookie = new Cookie();
			querycookie.setDomain(innercookie.getDomain());
			querycookie.setName(innercookie.getName());
			querycookie.setPath(innercookie.getPath());
			querycookie.setValue(innercookie.getValue());
			querycookie.setUserid(user.getId());

			if(cookieService.selectCount(querycookie) ==0){
				querycookie.setHttponly(innercookie.isHttpOnly());
				querycookie.setSecure(innercookie.isSecure());
				querycookie.setValue(innercookie.getValue());
				querycookie.setExpires(innercookie.getExpires());
				cookieService.create(querycookie);
			}else{
				Cookie cookie = cookieService.find(querycookie);
				cookie.setHttponly(innercookie.isHttpOnly());
				cookie.setSecure(innercookie.isSecure());
				cookie.setValue(innercookie.getValue());
				cookie.setExpires(innercookie.getExpires());
				cookieService.updateByPrimaryKey(cookie);
			}
		}
	}

	private static void addCookies(CookieManager cookieManeger,List<Cookie> cookies){
		for(Cookie cookie:cookies){
			com.gargoylesoftware.htmlunit.util.Cookie innerCookie = 
					new com.gargoylesoftware.htmlunit.util.Cookie(cookie.getDomain(), 
							cookie.getName(),
							cookie.getValue(),
							cookie.getPath(),
							cookie.getExpires(),
							cookie.getSecure(),
							cookie.getHttponly());
			cookieManeger.addCookie(innerCookie);
		}
	}


	private void handleNetExecption(User user){
		if(user.getFailedtimes() > SystemInfo.testTimes){
			user.setStatus(UserSatus.OFFLINE.getStatus());
			user.setFailedtimes(0);
		}else{
			user.setFailedtimes(user.getFailedtimes() +1);
		}
		userService.updateByPrimaryKeySelective(user);
		List<Task> tasks = taskService.findByUserID(user.getId());
		for(Task task:tasks){
			task.setStatus(TaskStatus.INITIAL.getStatus());
			taskService.updateByPrimaryKeySelective(task);
		}
	}

	/*
	 * destnation website charset is gbk encoding,but our project is utf-8 encoding,
	 */
	private String gbk2utf8(String in){
		try {
			return new String(in.getBytes("gbk"),"utf-8");
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	public static void main(String args[]) throws HttpException, IOException{
		/*HttpInvoker httpInvoker = new HttpInvoker(WEBURL+"loginAction.do?zjh="
				+"2012141463200"+"&mm="+"043374",0,null);
		String resultHtml = new String(httpInvoker.request().getResponseBody(),"gbk");
		System.out.println(resultHtml);*/
		BeanFactory beanFacory = new ClassPathXmlApplicationContext("applicationContext.xml");
		CourseNetInvoker courseNetInvoker =   (CourseNetInvoker) beanFacory.getBean("courseNetInvoker");
		User user = new User();
		user.setStudentnumber("2012141463200");
		user.setPassword("043374");
		courseNetInvoker.userService.createSelective(user);
		courseNetInvoker.login(user.getId());

	
		Task task = new Task();
		ScuCourse scuCourse = new ScuCourse();
		scuCourse.setCoursenumber("311093040");
		scuCourse.setSycnumber("04");
		scuCourse.setLasdupdate(new Date());
		scuCourse.setLeftnumber(0);
		courseNetInvoker.scuCourseService.create(scuCourse);

		task.setCourseid(scuCourse.getId());
		task.setUserid(user.getId());
		task.setStatus(TaskStatus.INITIAL.getStatus());
		courseNetInvoker.taskService.createSelective(task);

		boolean flag = courseNetInvoker.selectCourse(task);
		System.out.println("选课结果"+flag);
		System.exit(0);
	}
}
